简介
实现线程同步,让多个线程排队依次获取某个资源,保证数据不会出错。
思考
synchronized 到底锁定是什么元素?
- 修饰方法
- 静态方法,锁的是类
- 非静态方法,锁的是调用方法的对象
- 修饰代码块.锁定的是传入的对象
== 使用sleep()方法是为了更好的查看执行顺序 ==
示例
修饰非静态方法
没有加synchronized关键字修饰的
public class Test {
public static void main(String[] args) {
TestData testData = new TestData();
new Thread(()->{
testData.methods01();
},"线程A").start();
new Thread(()->{
testData.methods02();
},"线程B").start();
/**
* 结果是:
* 线程B中的方法2执行了
* 线程A中的方法1执行了
* 结论: 显而易见,线程B并没有等待线程A调用结束后在调用.这是因为没有加锁,所以不用等待释放锁
*/
}
}
class TestData{
/**
* 创建两个没有锁的方法,但是让方法一睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*
*/
public void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
两个方法都加synchronized关键字修饰的
public class Test {
public static void main(String[] args) {
TestData testData = new TestData();
new Thread(()->{
testData.methods01();
},"线程A").start();
new Thread(()->{
testData.methods02();
},"线程B").start();
/**
* 结果是:
* 线程A中的方法1执行了
* 线程B中的方法2执行了
* 结论: 显而易见,线程B等待线程A调用结束后在调用.
* 这是因为在非静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住这个调用方法的对象
* 而上面只创建了一个对象所以会出现当线程A进来的时候发现需要调用的方法1是加锁的,而且当前调用方法的对象没有被锁,
* 那么线程A就会把对象testData给锁住,然后开始执行. 在没有执行结束之前是不会放开的.当这个时候线程B进来了看见
* 需要调用的方法2是加锁的,但是这个时候调用方法的对象testData已经被线程A锁住了,那么线程B只能等线程A调用完毕,
* 释放对象后才可以实现线程B对这个对象testData加锁,然后执行方法2.
*/
}
}
class TestData{
/**
* 创建两个都加锁的方法,但是让方法一睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*
*/
public synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public synchronized void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
只有一个方法加synchronized关键字修饰的
public class Test {
public static void main(String[] args) {
TestData testData = new TestData();
new Thread(()->{
testData.methods01();
},"线程A").start();
new Thread(()->{
testData.methods02();
},"线程B").start();
/**
* 结果是:
* 线程B中的方法2执行了
* 线程A中的方法1执行了
* 结论: 显而易见,线程B并没有等待线程A调用结束后在调用.
* 这是因为在非静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住这个调用方法的对象
* 而上面虽然只创建了一个对象所以会出现当线程A进来的时候发现需要调用的方法1是加锁的,而且当前调用方法的对象没有被锁,
* 那么线程A就会把对象testData给锁住,然后开始执行. 在没有执行结束之前是不会放开的.
* 当这个时候线程B进来了看见需要调用的方法2是没有加锁的,也就不需要锁住调用方法的对象,所以不用管这个对象是否被锁住了,
* 直接就可以调用方法并执行
*/
}
}
class TestData{
/**
* 创建两个的方法,但是只让方法一加锁并睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*/
public synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
当new两个调用加锁方法的对象去执行不同的加锁方法
public class Test {
public static void main(String[] args) {
TestData testData01 = new TestData();
TestData testData02 = new TestData();
new Thread(()->{
testData01.methods01();
},"线程A").start();
new Thread(()->{
testData02.methods02();
},"线程B").start();
/**
* 结果是:
* 线程B中的方法2执行了
* 线程A中的方法1执行了
* 结论: 显而易见,线程B并没有等待线程A调用结束后在调用.
* 这是因为在非静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住这个调用方法的对象.
* 而上面new了两个对象,所以线程A锁住的是testData01对象,但是当线程B对象去调用方法2时锁住的是testData02对象
* 所以线程B不用等待线程A释放对象,释放锁
*/
}
}
class TestData{
/**
* 创建两个的方法,但是只让方法一加锁并睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*/
public synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public synchronized void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
修饰静态方法
不加synchronized关键字的我就不演示
两个静态方法都加上synchronized关键字修饰
public class Test {
public static void main(String[] args) {
new Thread(()->{
TestData.methods01();
},"线程A").start();
new Thread(()->{
TestData.methods02();
},"线程B").start();
/**
* 结果是:
* 线程A中的方法1执行了
* 线程B中的方法2执行了
* 结论: 显而易见,线程B等待线程A调用结束后在调用.
* 这是因为在静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住当前类
* 就会出现线程B去调用的时候发现线程A已经锁住了类,故此需要等待线程A释放
*/
}
}
class TestData{
/**
* 创建两个的方法,但是只让方法一加锁并睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*/
public static synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public static synchronized void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
只有一个静态方法加上synchronized关键字修饰
public class Test {
public static void main(String[] args) {
new Thread(()->{
TestData.methods01();
},"线程A").start();
new Thread(()->{
TestData.methods02();
},"线程B").start();
/**
* 结果是:
* 线程B中的方法2执行了
* 线程A中的方法1执行了
* 结论: 显而易见,线程B并没有等待线程A调用结束后在调用.
* 这是因为在静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住当前类
* 就会出现线程B去调用的时候发现需要调用的方法2不需要加锁,故此不用锁类,也就不用等待线程A释放
*/
}
}
class TestData{
/**
* 创建两个的方法,但是只让方法一加锁并睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*/
public static synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public static void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
实例化两个对象去分别调用加锁的方法
不建议使用类的实例化访问静态成员
public class Test {
public static void main(String[] args) {
TestData testData01 = new TestData();
TestData testData02 = new TestData();
new Thread(()->{
testData01.methods01();
},"线程A").start();
new Thread(()->{
testData02.methods02();
},"线程B").start();
/**
* 结果是:
* 线程A中的方法1执行了
* 线程B中的方法2执行了
* 结论: 显而易见,线程B等待线程A调用结束后在调用.
* 这是因为在静态方法上加的synchronized,当线程去调用这个加了锁的方法的时候会去锁住当前类
* 由于锁的是这个类,不是调用方法的实例化对象,故此无论创建多少个实例化,只要调用的方法是加上了synchronized
* 关键字的,锁的都是一个类,不是实例化对象
*/
}
}
class TestData{
/**
* 创建两个的方法,但是只让方法一加锁并睡眠3秒,看看只调用方法2的线程B会不会等待调用方法1的线程A调用结束后在调用
*/
public static synchronized void methods01(){
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"中的方法1执行了");
}
public static synchronized void methods02(){
System.out.println(Thread.currentThread().getName()+"中的方法2执行了");
}
}
对代码进行加锁
实例化一个对象的前提下,没有对代码块进行加锁,多线程的执行结果
public class Test {
public static void main(String[] args) {
TestData testData = new TestData();
for (int i = 0; i < 3; i++) {
new Thread(()->{
testData.methods1();
}).start();
}
/**
* 结果是:
* 开始...
* 开始...
* 开始...
* 结束...
* 结束...
* 结束...
* 结论: 显而易见,线程之间并没有排队执行
* 这是因为在代码块上没有加synchronized,所以不会按照顺序执行
*/
}
}
class TestData{
public void methods1(){
/**
* 创建没有加锁的代码块,看看多线程调用的时候会不会排队执行
*/
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
实例化一个对象的前提下,创建加锁的代码块,并且锁的参数是this,(这个this是指当前对象)
public class Test {
public static void main(String[] args) {
TestData testData = new TestData();
for (int i = 0; i < 3; i++) {
new Thread(()->{
testData.methods1();
}).start();
}
/**
* 结果是:
* 开始...
* 结束...
* 开始...
* 结束...
* 开始...
* 结束...
* 结论: 显而易见,线程之间有排队执行
* 这是因为在代码块上加了synchronized,且参数是this,所以会按照顺序执行
*/
}
}
class TestData{
public void methods1(){
/**
* 创建加锁的代码块,并且锁的参数是this,(这个this是指当前对象)看多线程调用的时候会不会排队执行
*/
synchronized (this){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}
在实例化多个对象时,创建加锁的代码块,并且锁的参数是this,(这个this是指当前对象)
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
TestData testData = new TestData();
new Thread(()->{
testData.methods1();
}).start();
}
/**
* 结果是:
* 开始...
* 开始...
* 开始...
* 结束...
* 结束...
* 结束...
* 结论: 显而易见,线程之间没有排队执行
* 这是因为在代码块上加了synchronized,但是参数是this,this指的当前对象
* 创建线程调用时实例化了对应数量的对象,且调方法的对象是不同的,故此线程之前不用等待其他线程释放锁,
* 因为其他线程加的锁的对象和当前调用方法的对象不是一个对象
*/
}
}
class TestData{
public void methods1(){
/**
* 创建加锁的代码块,并且锁的参数是this,(这个this是指当前对象)看多线程调用的时候会不会排队执行
*/
synchronized (this){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}
创建加锁的代码块,并且锁的参数是当前类,(TestData.class)看多线程调用的时候会不会排队执行
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
TestData testData = new TestData();
new Thread(()->{
testData.methods1();
}).start();
}
/**
* 结果是:
* 开始...
* 结束...
* 开始...
* 结束...
* 开始...
* 结束...
* 结论: 显而易见,线程之间有排队执行
* 这是因为在代码块上加了synchronized,但是参数是当前类(TestData.class)
* 即使创建线程调用时实例化了对应数量的对象,且调方法的对象是不同的,
* 但由于需要锁的是类,不是实例化对象,所以无论实例化多少个对象,都是需要等待上一个线程释放的
*/
}
}
class TestData{
public void methods1(){
/**
* 创建加锁的代码块,并且锁的参数是当前类,(TestData.class)看多线程调用的时候会不会排队执行
*/
synchronized (TestData.class){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}
创建加锁的代码块,并且锁的参数是值(Integer num = 1),看多线程调用的时候会不会排队执行
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
TestData testData = new TestData();
new Thread(()->{
testData.methods1();
}).start();
}
/**
* 结果是:
* 开始...
* 结束...
* 开始...
* 结束...
* 开始...
* 结束...
* 结论: 显而易见,线程之间有排队执行
* 这是因为在代码块上加了synchronized,但是参数是值(Integer num = 1)
* 这时候Integer num = 1是唯一的,所以每次线程进来锁的都是这个Integer num = 1
* 故此是需要等待上一个线程释放的
*/
}
}
class TestData{
public void methods1(){
/**
* 创建加锁的代码块,并且锁的参数是值(Integer num = 1),看多线程调用的时候会不会排队执行
*/
Integer num = 1;
synchronized (num){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}
创建加锁的代码块,并且锁的参数是外部传进来的可以变值(Integer num ),看多线程调用的时候会不会排队执行
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
TestData testData = new TestData();
Integer num = new Integer(1);
new Thread(()->{
testData.methods1(num);
}).start();
}
/**
* 结果是:
* 开始...
* 开始...
* 开始...
* 结束...
* 结束...
* 结束...
* 结论: 显而易见,线程之间并没有排队执行
* 这是因为在代码块上加了synchronized,但是参数是调用方法传进来的参数,循环传进来3个
* 这时候Integer num 不是唯一的,所以每次线程进来锁的不是同一个Integer num
* 故此是不需要等待上一个线程释放的
*/
}
}
class TestData{
public void methods1(Integer num){
/**
* 创建加锁的代码块,并且锁的参数是外部传进来的可以变值(Integer num ),看多线程调用的时候会不会排队执行
*/
synchronized (num){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}
创建加锁的代码块,并且锁的参数是外部传进来的Integer常量池中 (-128 ~ 127),看多线程调用的时候会不会排队执行
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
TestData testData = new TestData();
Integer num = 18;
new Thread(()->{
testData.methods1(num);
}).start();
}
/**
* 结果是:
* 开始...
* 结束...
* 开始...
* 结束...
* 开始...
* 结束...
* 结论: 显而易见,线程之间有排队执行
* 这是因为在代码块上加了synchronized,参数是调用方法传进来的参数,虽然循环传进来3个
* 但是Integer有自己的常量池 -128 ~ 127,在这个范围内使用的都是直接从常量池中取的同一个值
* 故此是唯一的,所以需要等待上一个线程释放的
*
* 但是超出-128 ~ 127,就是实例化的了,就不是唯一的,就不需要等待上一个线程释放
*/
}
}
class TestData{
public void methods1(Integer num){
/**
* 创建加锁的代码块,并且锁的参数是外部传进来的Integer常量池中 (-128 ~ 127),看多线程调用的时候会不会排队执行
*/
synchronized (num){
System.out.println("开始...");
try {
// 睡眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束...");
}
}
}