线程,同步
01.第一章:线程_多线程原理:
1).定义线程:
class MyThread extends Thread{
public void run(){
for(int i = 0; i < 100 ; i++){
System.out.println("i = " + i);
}
}
}
2).启动线程:
main(){
MyThread t1 = new MyThread();
t1.start();
for(int k = 0; k < 100 ; k++){
System.out.println("k = " + k);
}
}
3).多个线程的运行原理:
1).当主线程启动一个独立的线程后,这个独立的线程会与主线程“同时”运行;
2).但对于“单核,单颗”的CPU来讲,在某一个“时间点”上,只能有一个线程去执行,执行一小段时间后,会立即切换到另一个线程。CPU始终会在这两个线程间频繁的、快速的切换。
02.第一章:线程创建线程的方式一继承Thread类及常用方法:
1).自定义线程类,继承自Thread;
2).重写run()方法(将线程中要做的事情写在这里)
3).启动线程:
1).创建自定义线程类的对象;
2).调用对象的start()方法:
示例代码:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
for (int k = 0; k < 100; k++) {
System.out.println("k = " + k);
}
}
}
注意事项:
1).对于“一个线程类”,可以创建多个“线程对象”,每个线程对象都可以单独启动;
2).对于“一个线程对象”,只能调用一次start(),不能多次start();
3).重写的是run(),但启动线程调用的是start();
4).Thread类中的常用方法:
1).public String getName():获取线程名称;
注:每个线程对象都有一个默认的线程名称:Thread - 索引值
2).public void setName(String name):设置线程名称:
示例代码:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + " i = " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("章子怡");
t2.setName("汪峰");
t1.start();
t2.start();
for (int k = 0; k < 100; k++) {
System.out.println(Thread.currentThread().getName() + " k = " + k);
}
}
}
3).public static Thread currentThread():获取当前的线程对象;示例代码:
main(){
System.out.println("主线程名:" + Thread.currentThread().getName());
}
4).public static void sleep(long m):让当前的线程休眠指定的毫秒数;
示例代码:
public class Demo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = sdf.format(date);
System.out.println(str);
//休息1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
03.第一章:线程创建线程的方式二实现Runnable接口及特点:
步骤:
1).自定义类实现Runnable接口;
2).重写run()方法;
3).启动线程:
1).创建一个自定义类对象。
2).创建一个Thread对象:并将自定义对象作为参数传给Thread的构造方法;
public Thread (Runnable target)
3).调用Thread对象的start()方法启动线程;
示例代码:
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyRunnable myRun = new MyRunnable();
Thread t1 = new Thread(myRun);
t1.start();
for (int k = 0; k < 100; k++) {
System.out.println("k = " + k);
}
}
}
04.第一章:线程_两种方式的区别:
1).第一种需要继承自Thread--(由于Java是单继承,对子类就有一定的限制)
2).第二种实现Runnable接口--对于子类比较灵活(建议使用)
05.第一章:线程_匿名内部类的方式实现线程:
1).格式一:Thread的子类:
public class Demo {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}.start();
for (int k = 0; k < 100; k++) {
System.out.println("k = " + k);
}
}
}
2).格式二:Runnable的子类:
public class Demo {
public static void main(String[] args) {
//2.方式二:Runnable的子类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}).start();
for (int k = 0; k < 100; k++) {
System.out.println("k = " + k);
}
}
}
06.第二章:线程安全_多线程的安全性问题:
1).Runnable接口方式的特点:
2).当Runable的run()中访问同一个变量的时候:
3).多个线程共同访问“同一个共享资源”时,这个共享资源就收到并发访问。会造成共享资源的最终结果不正确,不一致。
例如:上例和多线程售票(demo07)
07.第二章:线程安全线程同步解决多线程的安全性问题同步代码块:
1).同步代码块是用在:被并发访问的那个“方法中”;
2).语法:
synchronized(锁对象){
//同步代码
}
3).锁对象:可以是任何对象;
4).示例代码:
public class Tickets implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
try {
Thread.sleep(1);//目的:让其他线程有机会进入同步代码块取票
}catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {//窗口3(排队),窗口1(排队)
//窗口2(关门--拥有锁)
if(tickets > 0){
System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
tickets--;
}else{
System.out.println("没票了,都回去吧!");
break;
}
}
//窗口2执行完毕(开门--释放锁)
}
}
}
08.第二章:线程安全线程同步解决多线程的安全性问题同步方法【常用】
public class Tickets implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
getTicket();
}
}
public synchronized void getTicket() {//窗口3(等待),窗口1(等待)
if(tickets > 0){//窗口2(加锁)
System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
tickets--;
}else{
System.out.println("没票了,都回去吧!");
System.exit(0);
}
}
}
1).说明:同步方法,可以是“普通方法”也可以是“静态方法”
只要这个方法被多个线程同时访问,而程序需要每个线程必须执行完这个方法的所有步骤,才允许第二个线程进入,
这种情况下就可以将这个方法声明为“同步方法”,可以保证方法内访问的数据的安全性。
09.第二章:线程安全_线程同步解决多线程的安全性问题_Lock锁:
public class Tickets implements Runnable {
private int tickets = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(1);//目的:让其他线程有机会进入同步代码块取票
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();//(加锁)//窗口3(排队),窗口1(排队)
try {
//窗口2(关门--拥有锁)
if (tickets > 0) {
System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
tickets--;
} else {
System.out.println("没票了,都回去吧!");
break;
}
}finally {
lock.unlock();//窗口2执行完毕(开门--释放锁)
}
}
}
}
10.第三章:线程状态_概述:
1).new状态:新建状态;
MyThread t = new MyThread();
2).调用start():进入到:可运行状态;(交个操作系统,并不马上运行)
3).操作系统允许线程运行,但发现被访问的方法“被锁”:进入到:锁阻塞;
4).允许运行,被无限等待(Object --> wait()):进入到:无限等待(等待被唤醒)
5).允许运行,执行sleep(1000),进入到:计时等待状态
6).run运行完毕,进入到:被终止状态(变为垃圾,等待被回收)
11.第三章:线程状态_等待与唤醒:
public class Demo {
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj){
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 20) {
try {
System.out.println("第一个线程开始等待,等待唤醒....");
obj.wait();//让当前持有这把锁的线程:等待(挂起--暂停),会释放锁
System.out.println("第一个线程醒来啦,开始继续运行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
Thread.sleep(1000);//休息1秒:目的:保证让第一个线程先执行,先获取锁
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("第二个线程,等待5秒,唤醒第一个线程....");
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
obj.notify();
// obj.notifyAll();
}
}
}).start();
}
}
12.第三章:线程状态_状态图:
总结:
01.能够描述Java中多线程运行原理
当多个线程同时执行时:
1).每个线程都会有一个独立的“栈”空间;
2).这些“栈空间”都是在“同时运行”;
3).同时访问CPU;
02.能够使用继承类的方式创建多线程
class MyThread extends Thread{
public void run(){
..
}
}
main(){
MyThread t = new MyThread();
t.start();
}
03.能够使用实现接口的方式创建多线程
class MyRunnable implements Runnable{
public void run(){
}
}
main(){
MyRunnable run = new MyRunnable();
Thread t1 = new Thread(run);
t1.start();
}
04.能够说出实现接口方式的好处
子类可以更灵活,还可以继承其它类;
05.能够解释安全问题的出现的原因
多个线程共同访问同一个共享资源;
06.能够使用同步代码块解决线程安全问题
Object obj = new Object();
public void getTicket(){
synchronized(obj){
…//同步代码块
}
}
07.能够使用同步方法解决线程安全问题
public synchronized void getTicket(){
//同步代码
}
上一篇: 我会手动创建线程,为什么让我使用线程池?
下一篇: 多线程(二)线程交互之互斥与同步