Java创建线程的方式:
两个线程同时加载一个类字节码的时候,对于JVM而言,天生的线程安全,因为类字节码只能加载一次,当第二次加载时会直接跳过
1. 继承java.lang.Thread
线程启动后,必须执行run方法(run方法运行完就结束)
public class TestThread1 extends Thread{ @Override public void run() { while(true){ System.out.println("Thread1"); } } }
main方法:
@Test public void Test1(){ TestThread1 t1 = new TestThread1();//t1就是一个线程,因为继承了Thread t1.start();//如果改成t1.run()的话就会变成单线程,只会执行t1中的方法 while(true){ //再创建一个 System.out.println("main方法"); } }
2. 实现Runable接口(建议使用这种方法,因为实现之后还可以继承类)
public class TestThread2 implements Runnable { @Override public void run() { while(true){ System.out.println("TestThread2"); } } }
main方法:
@Test public void Test2(){ TestThread2 t2 = new TestThread2(); Thread t = new Thread(t2); t.start(); //也可以采用匿名内部类的写法 Thread t3 = new Thread( new Runnable() { @Override public void run() { while(true){ System.out.println("Thread3"); } } } ); t3.start(); }
------------------------手动分隔符=。=-------------------------------------------------------------------------------------------------------
多线程几个常用的方法
Thread.sleep 当前线程休眠一会
getName 返回线程名字
setName 给线程取个名字
interrup() 中断线程
t.join() 等待t进程跑完后再去执行其他的线程
yield() 让别的线程先执行
示例1:
public class TestThread1 extends Thread{ @Override public void run() { while(true){ System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { //这个异常是指这个进程正睡着呢,其他进程就把它叫醒 e.printStackTrace(); } } } }main方法:
@Test public void Test1() throws InterruptedException { TestThread1 t1 = new TestThread1(); t1.setName("t_shiyw"); t1.setPriority(Thread.MAX_PRIORITY);//Thread.MAX_PRIORITY=10 t1.start(); t1.join(); //加上这行就不会执行下面的mian线程 while(true){ System.out.println("main线程"); } }
------------------------手动分隔符=。=---------------------------------------------------------------------------------------------------
怎么处理线程安全
synchronized
锁就是一个对象
如果和static在一起,那么这个锁,就是Test.class(Test类字节码,在java虚拟机中独一份)
如果没有static,那么这个锁就是new出来的实力对象
示例1:
public class TestThread2 implements Runnable { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { //这个异常是指这个进程正睡着呢,其他进程就把它叫醒 e.printStackTrace(); } TestThread.i = TestThread.i+1; } }
main方法:
ublic class TestThread { public static int i = 0; @Test public void Test1() throws InterruptedException { TestThread2 t = new TestThread2(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); Thread t5 = new Thread(t); Thread t6 = new Thread(t); Thread t7 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); Thread.sleep(2000); System.out.println(i); } }
运行结果:
可能是7,可能是6,可能是5。。。。
如果改成这样:
public class TestThread2 implements Runnable { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { //这个异常是指这个进程正睡着呢,其他进程就把它叫醒 e.printStackTrace(); } TestThread.add(); } }
main方法:
public class TestThread { public static int i = 0; @Test public void Test1() throws InterruptedException { TestThread2 t = new TestThread2(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); Thread t5 = new Thread(t); Thread t6 = new Thread(t); Thread t7 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); Thread.sleep(2000); System.out.println(i); } public static synchronized void add() { i++; } }
运行结果:
肯定是7
什么时候会有线程安全,几个线程访问同一个对象时。
Java中两个线程是否可以同是否问一个对象的两个不同的synchronized方法?
不可以!!
多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同一个锁某个时刻只能被一个执行线程所获取, 因此其他线程都得等待锁的释放. 因此就算你有多余的cpu可以执行, 但是你没有锁, 所以你还是不能进入synchronized方法执行, CPU因此而空闲. 如果某个线程长期持有一个竞争激烈的锁, 那么将导致其他线程都因等待所的释放而被挂起, 从而导致CPU无法得到利用, 系统吞吐量低下. 因此要尽量避免某个线程对锁的长期占有 !
示例:
public class T1 implements Runnable{ @Override public void run() { TestThread3.println1(); } }
public class T2 implements Runnable{ @Override public void run() { TestThread3.println2(); } }
main方法:
public class TestThread3 { public static void main(String[] args){ T1 t1 = new T1(); T2 t2 = new T2(); Thread thread1 = new Thread(t1); Thread thread2 = new Thread(t2); thread1.start(); thread2.start(); } public static synchronized void println1(){ for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static synchronized void println2(){ for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
运行结果:
Thread-1:1
.
.
.
Thread-1:99
Thread-0:1
.
.
.
Thread-0:99
如果将上面TestThread3中的println2方法的static去掉。T2类改成如下:
public class T2 implements Runnable{ @Override public void run() { TestThread3 t = new TestThread3(); t.println2(); } }
运行结果:
Thread-0和Thread-1交叉执行,不会实现线程同步
原因:
new TestThread3()是一个Object对象,TestThread3.class也是Object对象,但不是同一个对象,一个在堆里面,一个在方法区类加载的时候就有了。
所以加了static抢到的是类字节的锁,不加static,抢到的是实例锁。
------------------------手动分隔符=。=-----------------------------------------------------------------------------------------------------------------
wait 当前线程释放锁,并且进入长期休眠状态,等待别人叫醒
sleep 不会释放锁
notify
唤醒
当前对象上的等待线程,notify唤醒单个线程;notifyAll唤醒所有线程
示例:
生产者,消费者,馒头,篮子 生产者生产馒头 消费者吃馒头
篮子:
public class Box { //因为访问的是同一个篮子,所以要注意线程安全 private Mantou[] arr = new Mantou[10];//最多10个馒头 private int i = 0; //标志现在是哪个馒头 //放馒头 public synchronized void set(Mantou mantou){ while(i>=10){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"生产了"+mantou); arr[i] = mantou; i++; this.notifyAll(); } //吃馒头 public synchronized Mantou pop() { while(i==0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } i--; Mantou mantou = arr[i]; System.out.println(Thread.currentThread().getName()+"消费了"+mantou); this.notifyAll(); return mantou; } }
馒头:
public class Mantou { private String name; public Mantou(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString(){ return this.name; } }
消费者:
public class Consumer implements Runnable { private Box box; @Override public void run() { while (true) { Mantou mantou = box.pop(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public void setBox(Box box) { this.box = box; } }
生产者:
public class Producter implements Runnable { private Box box; @Override public void run() { while(true){ Mantou mantou = new Mantou("馒头"+(int)(Math.random()*100)); box.set(mantou); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void setBox(Box box) { //用set方法告诉他篮子在哪里 this.box = box; } }
main方法:
public class Test { public static void main(String args[]) { Producter p = new Producter(); Consumer c = new Consumer(); Box box = new Box(); p.setBox(box); c.setBox(box); Thread p1 = new Thread(p); p1.setName("生产者1"); Thread p2 = new Thread(p); p2.setName("生产者2"); Thread p3 = new Thread(p); p3.setName("生产者3"); Thread c1 = new Thread(c); c1.setName("消费者1"); Thread c2 = new Thread(c); c2.setName("消费者2"); Thread c3 = new Thread(c); c3.setName("消费者3"); Thread c4 = new Thread(c); c4.setName("消费者4"); p1.start(); p2.start(); p3.start(); c1.start(); c2.start(); c3.start(); c4.start(); } }
运行结果:
生产者1生产了馒头35
生产者3生产了馒头28
生产者2生产了馒头17
消费者1消费了馒头17
消费者2消费了馒头28
消费者3消费了馒头35
生产者3生产了馒头16
消费者3消费了馒头16
生产者1生产了馒头31
消费者4消费了馒头31
生产者2生产了馒头57
消费者2消费了馒头57
生产者3生产了馒头28
生产者2生产了馒头17
消费者1消费了馒头17
消费者2消费了馒头28
消费者3消费了馒头35
生产者3生产了馒头16
消费者3消费了馒头16
生产者1生产了馒头31
消费者4消费了馒头31
生产者2生产了馒头57
消费者2消费了馒头57