一.关于同步
1.什么情况下需要同步
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
2.同步代码块使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
3.非静态方法的锁对象是: this
4.静态方法的锁对象是:字节码对象,也就是这个方法对应所在的那个类的.class对象,
package com.fenqing.duoxiancheng;
public class d11_Synchronized {
public static void main(String[] args) {
final Printer p=new Printer();
new Thread(){
public void run(){
while(true){
p.print1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
p.print2();
}
}
}.start();
}
}
class Printer {
static demo d = new demo();
public static void print1() { //方法是static,所以锁对象也要用static修饰
//synchronized(new demo()){ //不能用匿名对象,因为匿名对象不是同一个对象,也就是说不是同一把锁
synchronized(d){ //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,
System.out.print("计");
System.out.print("算");
System.out.print("机");
System.out.print("\r\n");
}
}
public static void print2() {
synchronized(d){
System.out.print("软");
System.out.print("件");
System.out.print("工");
System.out.print("程");
System.out.print("\r\n");
}
}
public synchronized void print3(){ //定义同步方法
System.out.print("专");
System.out.print("业");
System.out.print("\r\n");
}
}
class demo{
}
线程安全性
- 多线程并发操作同一数据时, 就有可能出现线程安全问题
- 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
关于同步的练习
四个窗口出售100张票的问题。
1,用Thread()的构造实现
package com.fenqing.duoxiancheng;
public class d12_sell {
public static void main(String[] args) {
TicketsSeller t1 = new TicketsSeller();
TicketsSeller t2 = new TicketsSeller();
TicketsSeller t3 = new TicketsSeller();
TicketsSeller t4 = new TicketsSeller();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t4.setName("窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketsSeller extends Thread {
private static int tickets = 100;
static Object obj = new Object();
public TicketsSeller() {
super();
}
public TicketsSeller(String name) {
super(name);
}
public void run() {
while(true) {
synchronized(obj) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...这是第" + tickets-- + "号票");
}
}
}
}
2.用Runnable实现
package com.fenqing.duoxiancheng;
public class d13_sell {
public static void main(String[] args) {
Ticket t=new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class Ticket implements Runnable {
private int tickets = 100;
public void run() {
while(true) {
synchronized(Ticket.class) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...这是第" + tickets-- + "号票");
}
}
}
}
死锁——哲学家进餐的问题
- 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁。
不清楚这个问题的可以看看哲学家就餐问题简介
package com.fenqing.duoxiancheng;
public class d14_deathLock {
private static String s1 = "筷子左"; //定义两个变量
private static String s2 = "筷子右";
public static void main(String[] args) {
new Thread() {
public void run() {
while(true) {
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "开吃");
}
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "开吃");
}
}
}
}
}.start();
}
}