需求:一台机器有3类数据,总共有多台机器,开启多个线程分别去不同机器获取数据。要求即快速又准确。
心得:
- 使用
synchronized
包装出现资源同步问题的临界区
- 在java中使用Map要使用线程安全的
ConcurrentHashMap
- 在java中使用List要使用
List<T> sysList = Collections.synchronizedList(new ArrayList<>());
使用说明:
有关操作系统中线程、进程、死锁、同步、互斥的概念,请看
操作系统之进程管理
关于synchronized的使用
Synchronized的作用
- 确保线程互斥的访问同步代码
- 保证共享变量的修改能够及时可见
- 有效解决重排序问题
1.普通同步方法,锁的是当前实例对象
与2区别在使用声明那里
package ink.poesy.service;
/**
* @author: WenLeiWang
* Created in 2019/11/20 11:39
*/
public class SynchronizedUse implements Runnable{
//共享资源(临界资源)
static int i=0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
}
创建main调用创建实例调用上面编写的方法
public static void main(String[] args) throws InterruptedException {
SynchronizedUse instance=new SynchronizedUse();
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
/**
* 输出结果:
* 2000000
*/
但是如果这么编写代码的话
public static void main(String[] args) throws InterruptedException {
SynchronizedUse instance=new SynchronizedUse();
SynchronizedUse instance2=new SynchronizedUse();
Thread t1=new Thread(instance);
Thread t2=new Thread(instance2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
//多次执行
/**
* 输出结果:
* 1994457
*/
/**
* 输出结果:
* 1959761
*/
出现了同步问题,t1和t2都会进入各自的对象锁,也就是说t1和t2线程使用的是不同的锁,因此线程安全是无法保证的。因此要注意上面的方法不能创建多个对象来访问对象里的公共资源,不然锁失效。也同样验证了这样锁的只是当前实例的对象。
2.静态同步方法,锁的是当前类的class对象
同样是锁在了方法上,方法是静态的。
package ink.poesy.service;
/**
* @author: WenLeiWang
* Created in 2019/11/20 11:39
*/
public class SynchronizedUse implements Runnable{
static int i=0;
static int j=0;
/**
* 作用于静态方法,锁是当前class对象,也就是
* AccountingSyncClass类对应的class对象
*/
public static synchronized void increase(){
i++;
}
/**
* 非静态,访问时锁不一样不会发生互斥
*/
public synchronized void increase4Obj(){
j++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
increase4Obj();
}
}
public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new SynchronizedUse());
//new心事了
Thread t2=new Thread(new SynchronizedUse());
//启动线程
t1.start();t2.start();
t1.join();t2.join();
System.out.println("操作i:"+i);
System.out.println("操作j:"+j);
}
}
执行结果:
做100000次循环,正常来说结果是200000。
由于synchronized关键字修饰的是静态increase方法,与修饰实例方法不同的是,其锁对象是当前类的class对象。注意代码中的increase4Obj方法是实例方法,其对象锁是当前实例对象,如果别的线程调用该方法,将不会产生互斥现象,毕竟锁对象不同,但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。
3.同步方法块,锁是括号里面的对象
注释位置表示同步方法块,代码如下
package ink.poesy.service;
/**
* @author: WenLeiWang
* Created in 2019/11/20 11:39
*/
public class SynchronizedUse implements Runnable{
static SynchronizedUse inc = new SynchronizedUse();
static int i=0;
static int j=0;
static int k=0;
/**
* 作用于静态方法,锁是当前class对象,也就是
* AccountingSyncClass类对应的class对象
*/
public static synchronized void increase(){
i++;
}
/**
* 非静态,访问时锁不一样不会发生互斥
*/
public synchronized void increase4Obj(){
j++;
}
public void increase2Obj(){
//synchronized (inc){
k++;
//}
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
increase4Obj();
increase2Obj();
}
}
public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new SynchronizedUse());
//new心事了
Thread t2=new Thread(new SynchronizedUse());
Thread t3=new Thread(inc);
//启动线程
t1.start();t2.start();t3.start();
t1.join();t2.join();t3.join();
System.out.println("操作i:"+i);
System.out.println("操作j:"+j);
System.out.println("操作k:"+k);
}
}
有注释结果:k是此次测试的目标结果
放开注释的结果,即加上块锁: