一、实例变量
自定义线程类中的实例变量支针对其他线程可以与共享和不共享之分。
这在多个线程之间进行交互是很重要的技术点
不共享数据的情况

package com.it.po.thread01; public class MyThread04 extends Thread{ private int count=5; public MyThread04(String name) { super(name); this.setName(name);//设置线程名称 } @Override public void run() { super.run(); while (count>0){ count--;
System.out.println("由线程:"+this.getName()+" 计算,count= "+count);
} } }
package com.it.po.thread01; public class Run04 { public static void main(String[] args){ MyThread04 a = new MyThread04("A"); MyThread04 b = new MyThread04("B"); MyThread04 c = new MyThread04("C"); a.start(); b.start(); c.start(); } }

每一个线程有一个变量count,这种情况不存在变量共享,
以下实现多个线程对一个变量进行减的操作

(一)线程不安全情况
多个线程操作一个变量,比如火车站买票的情况
package com.it.po.thread01; public class MyThread05 extends Thread{ private static int count=5; public MyThread05(String name) { super(name); this.setName(name);//设置线程名称 } @Override public void run() { //此处不要用for循环,否则将是一个线程循环完,其他线程没有机会运行 super.run(); count--; System.out.println("由线程:"+this.getName()+" 计算,count= "+count); } }
package com.it.po.thread01; public class Run05 { public static void main(String[] args){ Thread a = new Thread(new MyThread05("A")); Thread b = new Thread(new MyThread05("B")); Thread c = new Thread(new MyThread05("C")); Thread d = new Thread(new MyThread05("D")); Thread e = new Thread(new MyThread05("E")); a.start(); b.start(); c.start(); d.start(); e.start(); } }
由线程:A 计算,count= 4 由线程:C 计算,count= 3 由线程:D 计算,count= 2 由线程:E 计算,count= 0 由线程:B 计算,count= 0
发现线程E和B打印出0,说明他们同时对count进行处理,出现了线程不安全的情况,我们希望结果不重复
JVM在count--操作如下
1)驱动原来count的值
2)count-1;
3)对count进行赋值,
三个步骤出现多线程访问一定会出现非线程安全问题
(二)线程安全情况
添加 synchronized 关键字
package com.it.po.thread01; public class MyThread05 extends Thread{ private static int count=5; public MyThread05(String name) { super(name); this.setName(name);//设置线程名称 } @Override synchronized public void run() { //此处不要用for循环,否则将是一个线程循环完,其他线程没有机会运行 super.run(); count--; System.out.println("由线程:"+this.getName()+" 计算,count= "+count); } }
由线程:B 计算,count= 4 由线程:C 计算,count= 3 由线程:A 计算,count= 2 由线程:D 计算,count= 1 由线程:E 计算,count= 0
synchronized 是对任意的对象及方法加上锁,当一个线程调用rrun()前先判断该run方法有没有被上锁,
如果被上锁了,说明有其他线程在执行该方法,必须等待其他线程执行完成之后才可以执行。
加锁的地方是互斥区 ,当run()被锁住了则多个线程会抢这个锁,抢到了才能执行。
方式二。
package com.it.po.thread01; public class MyThread05 extends Thread{ private static int count=5; public MyThread05(String name) { super(name); this.setName(name);//设置线程名称 } @Override public void run() { //此处不要用for循环,否则将是一个线程循环完,其他线程没有机会运行 synchronized(this) { super.run(); count--; System.out.println("由线程:" + this.getName() + " 计算,count= " + count); } } }
package com.it.po.thread01; public class Run05 { public static void main(String[] args){ Thread a = new Thread(new MyThread05("A")); Thread b = new Thread(new MyThread05("B")); Thread c = new Thread(new MyThread05("C")); Thread d = new Thread(new MyThread05("D")); Thread e = new Thread(new MyThread05("E")); a.start(); b.start(); c.start(); d.start(); e.start(); } }
由线程:B 计算,count= 4 由线程:C 计算,count= 3 由线程:A 计算,count= 2 由线程:E 计算,count= 1 由线程:D 计算,count= 0
(三)非线程安全情况,模拟servlet组件
package com.it.po.thread01; public class LoginServlet { private static String usernamePo; private static String passwordPo; public static void doPost(String username,String password){ try { usernamePo=username; passwordPo=password; if("a".equals(username)) { Thread.sleep(3000); } System.out.println("用户名:"+usernamePo+" 密码 "+passwordPo); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread01; public class Alogin extends Thread { @Override public void run() { super.run(); LoginServlet.doPost("a","lanpo"); } }
package com.it.po.thread01; public class Blogin extends Thread { @Override public void run() { super.run(); LoginServlet.doPost("b","lupo"); } }
package com.it.po.thread01; public class LoginRun { public static void main(String[] args){ Alogin a = new Alogin(); Blogin b = new Blogin(); a.start(); b.start(); } }
用户名:b 密码 lupo
用户名:b 密码 lupo
a用户休眠起来后用户名和密码都被b覆盖了
修改
package com.it.po.thread01; public class LoginServlet { private static String usernamePo; private static String passwordPo; synchronized public static void doPost(String username,String password){ try { usernamePo=username; passwordPo=password; if("a".equals(username)) { Thread.sleep(3000); } System.out.println("用户名:"+usernamePo+" 密码 "+passwordPo); } catch (InterruptedException e) { e.printStackTrace(); } } }
用户名:a 密码 lanpo
用户名:b 密码 lupo
(四)i-- 与System.out.println的异常
println()方法与i--其实可能会出现的异常
package com.it.po.thread01; public class MyThread06 extends Thread{ private int i=5; @Override public void run() { System.out.println(" i= "+(i--)+" 线程名称: "+Thread.currentThread().getName()); } }
package com.it.po.thread01; public class Run06 { public static void main(String[] args){ MyThread06 my = new MyThread06(); Thread t1 = new Thread(my); Thread t2 = new Thread(my); Thread t3 = new Thread(my); Thread t4 = new Thread(my); Thread t5 = new Thread(my); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
i= 5 线程名称: Thread-1 i= 4 线程名称: Thread-3 i= 5 线程名称: Thread-2 i= 3 线程名称: Thread-5 i= 2 线程名称: Thread-4
虽然println()方法是线程安全的,但是,i--是在进入println方法之前发生的,所以还是得用同步方法

浙公网安备 33010602011771号