线程安全
当多线程对共享资源同时曹组时,可能会发生数据的脏读、乱读现象,最终导致数据紊乱,是在多线程编程时必须要避免的问题。
实现线程同步操作
同步:要求多线程依次执行,当第一个线程在操作共享资源(执行一段代码),其他线程处于等待状态,到第一个线程处理完毕,其他线程中,获得CPU分配时间的那一个则可以操作共享资源(执行代码)
异步:多线程互相操作,互不干扰。
synchronize:同步操作
关键词的操作有三种方式:
1、作用在普通方法上
表示同一时间,只能有一个线程在执行该方法,等到此线程执行结束,获得CPU分配时间的那一个则可以操作共享资源(执行代码)
public class ThreadDemo01 {
public static void main(String[] args) {
final Table table = new Table();
//电脑1的桌子
Thread thread1 = new Thread(){
@Override
public void run() {
while(true){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//欢乐斗地主
String name =
Thread.currentThread().getName();
System.out.println(name+" 玩了一局欢乐豆,还剩:"+table.playGame());
}
}
};
//电脑2的桌子
Thread thread2 = new Thread(){
@Override
public void run() {
while(true){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//欢乐斗地主
String name =
Thread.currentThread().getName();
System.out.println(name+" 玩了一局欢乐豆,还剩:"+table.playGame());
}
}
};
thread1.start();
thread2.start();
}
}
class Table{
int beans = 1000;//系统第一次赠送1000个欢乐豆
public synchronized int playGame(){
if(beans <= 0){
//玩完了,game over。
throw new RuntimeException("欢乐豆不足,请充值...");
}
return beans -= 100;//每玩一局,系统扣除100欢乐豆
}
}
2、作用在代码块上(最常用)
如果仅对代码块实现同步效果,目的就是提高代码的多线程操作,缩小需要同步的范围,目的就是提高代码的运行效率
实现语法:
synchronize(共享的对象){
需要同步的代码块
}
共享对象: this , 静态OBject全局变量
public class ThreadDemo02 {
public static void main(String[] args) {
final Shop youYiKu = new Shop();
//客户1
Thread customer1 = new Thread(){
@Override
public void run() {
youYiKu.shopping(Thread.currentThread().getName());
}
};
//客户2
Thread customer2 = new Thread(){
@Override
public void run() {
youYiKu.shopping(Thread.currentThread().getName());
}
};
customer1.start();
customer2.start();
}
}
* 模拟客户进店挑选、试衣、结账离开效果。
class Shop{
public /*synchronized*/ void shopping(String peopleName){
try {
System.out.println(peopleName+"进店了....");
System.out.println(peopleName+"正在挑选衣服...");
Thread.sleep(5000);
/*
* 此优衣库只有一个试衣间
*/
synchronized(this){
System.out.println(peopleName+"挑选完毕,正在试衣...");
Thread.sleep(5000);
}
System.out.println(peopleName+"试衣完毕,结账离开。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、作用在静态方法上(不常用)
多线程共享操作的数据,属于类的,与对象无关,无论多少个对象,数据只有一份,多线程对静态数据操作,也可能会发生数据脏读、乱读现象,所以可以对静态方法实现同步。
结论:
同步(加锁)范围,适当就好
多线程的使用就是为了提高代码执行效率
不恰当的对代码、方法惊醒加锁,反而降低了代码的执行效率,不可取
所以同步范围决定着多线程代码的执行效率
线程中的五中状态
新建 new,待执行runnable,执行running,阻塞blocking,死亡dead
Object 中的其他方法
wait,notify,notifyAll
当需要某个进程后台执行,不影响主线程时,可以考虑用以下程序
模拟垃圾百度全家桶
public class ObjectMethodDemo04 extends Object{
public static void main(String[] args) {
final Object obj = new Object();
/*
* 搜狗输入法下载线程
*/
final Thread souGouDownLoad = new Thread(){
@Override
public void run() {
System.out.println("搜狗输入法下载 start...");
for(int i=1; i<=100; i++){
System.out.println("下载了 "+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("搜狗输入法下载 end...");
/*
* 要求搜狗输入法,在下载完毕时,就安装
* 百度安全认证的下载安装,是后台执行或者是
* 在搜狗输入法安装之后才执行。
*/
//要对共享的Object obj对象实现wait的线程唤醒操作
synchronized(obj){
// obj.notify();//唤醒被当前Object对象阻塞的线程。
obj.notifyAll();//唤醒被当前Object对阻塞的所有的线程。
}
/*
* 百度安全认证流氓下载、安装。
*/
System.out.println("百度安全认证下载 start...");
for(int i=1; i<=100; i++){
System.out.println("下载了 "+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("百度安全认证下载 end...");
System.out.println("百度安全认证安装 start...");
for(int i=1; i<=100; i++){
System.out.println("安装了 "+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("百度安全认证安装 end...");
}
};
/*
* 搜狗输入法安装线程
*/
Thread souGouFixed = new Thread(){
@Override
public void run() {
System.out.println("搜狗输入法安装 start...");
try {
synchronized(obj){
//先下载,才能安装
souGouDownLoad.start();
souGouDownLoad.join();
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1; i<=100; i++){
System.out.println("安装了 "+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("搜狗输入法安装 end...");
}
};
souGouFixed.start();
}
线程池
- 利用有效的线程数量,处理更多的任务。
- 有效的控制了内存的不必要消耗,提高服务器的性能。
- 线程池:限制线程数量的。
有规律的处理更多的任务。
public class ThreadPoolDemo05 {
public static void main(String[] args) {
ExecutorService threadPool =
Executors.newFixedThreadPool(2);
Runnable runnable = null;
for(int i=0; i<5; i++){
final int number = i+1;
runnable = new Runnable(){
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+
"在执行任务"+number);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
* 将任务交给线程池来管理,
* 线程池会分配不同的线程去执行任务。
*/
threadPool.execute(runnable);
}
//将线程池关闭
threadPool.shutdown();
}
}
将不安全的集合线程转换为安全的集合线程
public class SynchronizedCollectionDemo05 {
public static void main(String[] args) {
/*
* 线程非安全的List
* → 线程安全的List集合
*
* 通过Colleactions工具类中的集合同步方法
*/
List list = new ArrayList();
//非安全的list → 线程安全的list
list = Collections.synchronizedList(list);
/*
* 线程非安全的Set
* → 线程安全的Set集合
*
* 通过Colleactions工具类中的集合同步方法
*/
Set set = new HashSet();
//非安全的set → 线程安全的set
set = Collections.synchronizedSet(set);
/*
* 线程非安全的Map
* → 线程安全的Map集合
*
* 通过Colleactions工具类中的集合同步方法
*/
Map map = new HashMap();
//非安全的map → 线程安全的map
map = Collections.synchronizedMap(map);
}
}