1.线程中并发指一个时间段中多个线程都处于已启动但没有运行结束的状态。
多个线程之间默认并发运行,这种运行方式往往会出现交叉的情况。
public class Test {
public static void main(String[] args) {
new CounterThread("线程1").start();
new CounterThread("线程2").start();
}
}
class CounterThread extends Thread{
public CounterThread(String threadName){
super(threadName);
}
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(getName()+" : "+i);
}
}
}
运行结果
2.使原本并发运行的多个线程实现串行运行,就是先执行完一个线程,在执行另一个,需要通过对象锁机制来实现,synchronized就是一个利用锁实现线程同步的关键字。
public class Test {
private static Object shareData = new Object();//多线程间共享的数据
public static void main(String[] args) {
new CounterThread("线程1",shareData).start();
new CounterThread("线程2",shareData).start();
}
}
class CounterThread extends Thread{
private Object shareData;
public CounterThread(String threadName,Object shareData){
super(threadName);
this.shareData = shareData;
}
@Override
public void run() {
synchronized (shareData) {
for (int i = 0; i < 3; i++) {
System.out.println(getName() + " : " + i);
}
}
}
}
注意:
①synchronized对象锁(就是synchronized (shareData) synchronized后面括号里的内容,此时shareData就是对象锁):该对象锁是Java中创建的一个对象,该对象可由任意类创建,只要求所创建的对象在多个线程之间共享即可。如果对象锁为全局成员,为了保证该对象在多个线程间共享,该成员往往被private static final修饰。
②通过synchronized关键字实现了线程串行运行:一个线程执行完synchronized 代码块后另一个线程才执行,但是哪个线程先执行synchronized 代码块中的代码无法确定。
3.为什么通过synchronized就能实现多线程间串行运行呢?
被synchronized大括号括着的部分就是线程执行临界区,每次仅能有一个线程执行该临界区中的代码:当多个线程中的某个线程先拿到对象锁, 则该线程执行临界区内的代码,其他线程只能在临界区外部等待,当此线程执行完临界区中的代码后,在临界区外部等待的其他线程开始再次竞争以获取对象锁,进而执行临界区中的代码,但只能有一条线程“胜利”。
4.对象锁的内容,对象锁必须是同一个内容或者是唯一的一个内容,总之是共享的。
①
synchronized (this) //这个不可以,因为this表示的是调用这个run方法的对象,
//俩个线程是俩个对象调用的,所以是不同的对象,就不可以
要明白这两点:
谁调用该run方法?——CounterThread类对象;
谁执行该run方法?——正在执行的线程
②
synchronized (String.class) //这是唯一的一个,是共享的,所以可以,
//同一个xxx.class就可以
③
synchronized (" ") //引号里任意的内容都可以,因为存在常量池中,俩个对象,
//但是每次引号里的内容一样,就是同一个常量池中的地址,所以是共享的
④
package keeper2;
public class Program {
public static void main(String[] args) {
Object object= new Object();
CountThread countThread = new CountThread("1111",object);
countThread.start();
CountThread countThread1 = new CountThread("2222",object);
countThread1.start();
}
}
class CountThread extends Thread{
Object object;
CountThread(String name,Object object){
super();
this.object = object;
}
@Override
public void run() {
synchronized(object) { //俩次对象计数线程,但是object是同一个对象,所以是共享的
for(int i=0; i<100; i++) {
System.out.println(getName()+","+i);
}
}
}
}
⑤
public class Test {
public static void main(String[] args) {
Runnable counterThread = new CounterThread(); //接口实现类创建的对象
new Thread(counterThread,"线程1").start(); //创建线程
new Thread(counterThread,"线程2").start(); //再次创建线程,俩个不一样,但是接口实现类对象一样
}
}
class CounterThread implements Runnable {
@Override
public void run() {
synchronized (this) {// 此时临界区中的代码可以实现串行执行,因为此时接口实现类对象充当了对象锁的功能,该对象锁在两个线程之间共享,这this表示的是counterThread
Thread thread = Thread.currentThread();
for (int i = 0; i < 3; i++) {
System.out.println(thread.getName() + ":" + i);
}
}
}
}
⑥
static Object object = new Object();//该对象能在多个线程能共享,因为是静态的,比对象更先分配内存,俩个线程对象调用,但是指向的是同一块内存地址。
package keeper2;
public class Test {
public static void main(String[] args) {
new CountThread().start();
new CountThread().start();
}
}
class CountThread extends Thread{
static Object object = new Object();//该对象能在多个线程之间共享
@Override
public void run() {
synchronized (object) {
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "--->" + i);
}
}
}
}
5.死锁
public class DeadLockThread {
// 创建两个线程之间竞争使用的对象
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new ShareThread1().start();
new ShareThread2().start();
}
private static class ShareThread1 extends Thread {
public void run() {
synchronized (lock1) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("ShareThread1");
}
}
}
}
private static class ShareThread2 extends Thread {
public void run() {
synchronized (lock2) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("ShareThread2");
}
}
}
}
}
①如果有两个或两个以上的线程都访问了多个资源,而这些线程占用了一些资源的同时又在等待其它线程占用的资源,也就是说多个线程之间都持有了对方所需的资源,而又相互等待对方释放的资源,在这种情况下就会出现死锁。
②多个线程互相等待对方释放对象锁,此时就会出现死锁