熟练掌握API,能够完成并发编程->熟读API源码,掌握其原理->理解Java虚拟机的内存模型->操作系统对并发的支持
创建线程 多种方式
多线程运行 卖票
线程安全性问题引入
提出解决方案 synchronized
解决线程安全性问题
Synchronized 原理
…
1个线程1个顺序执行流.
多线程就是多个线程切换执行,并行是多个线程同时执行.
public class Thread1 extends Thread{
public Thread1(String name){
super(name);
}
@Override
public void run() {
//super.run();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1("t1");
Thread1 t2 = new Thread1("t2");
t1.start();
t2.start();
}
}
/**
* 多线程实现-实现Runnable
*/
public class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("Thread1-current->" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread2 runnable1 = new Thread2();
Thread t1 = new Thread(runnable1);
Thread t2 = new Thread(runnable1);
t1.start();
t2.start();
}
}
public class AnonymousInner {
/**
anonymous1 extend thread start ..
anonymous2 Runnable thread start ..
anonymous1and2 extend thread start ..
*/
public static void main(String[] args) {
anonymous1();
anonymous2();
anonymous1and2();
}
//继承Thread覆盖方式
public static void anonymous1() {
new Thread() {
public void run() {
System.out.println("anonymous1 extend thread start ..");
}
;
}.start();
}
//实现Runnable覆盖方式
public static void anonymous2() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("anonymous2 Runnable thread start ..");
}
}).start();
}
//继承Thread覆盖并且实现Runnable覆盖方式
//runnable的run也是传给thread,最后被thread的run覆盖
public static void anonymous1and2() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("anonymous1and2 Runnable thread start ..");
}
}) {
public void run() {
System.out.println("anonymous1and2 extend thread start .. ");
};
}.start();
}
}
//运行结果
anonymous1 extend thread start ..
anonymous2 Runnable thread start ..
anonymous1and2 extend thread start ..
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
//传入的是Integer类型,返回的就是Integer类型
public class NewThreadCallable implements Callable {
public static void main(String[] args) throws Exception {
NewThreadCallable d = new NewThreadCallable();
FutureTask task = new FutureTask<>(d);
Thread t = new Thread(task);
t.start();//启动的时候则call()开始执行了
System.out.println("我先干点别的。。。");
Integer result = task.get();
System.out.println("线程执行的结果为:" + result);
}
//类似run方法
@Override
public Integer call() throws Exception {
System.out.println("正在进行紧张的计算....");
Thread.sleep(3000);
return 1;
}
}
StatusThread1.java
StatusThread2.java
StatusThread3.java.
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。
它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
mythread.start()会启动一个新线程,并在新线程中运行run()方法。
而mythread.run()则会直接在当前线程中运行run()方法,并不会启动一个新线程来运行run()。
package bob.bob1;
public class RunAndStart {
public static void main(String[] args) {
Thread mythread = new MyThread("mythread");
System.out.println(Thread.currentThread().getName() + " call mythread.run()");
mythread.run();
System.out.println("----------------");
System.out.println(Thread.currentThread().getName() + " call mythread.start()");
mythread.start();
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
运行结果如下:
main call mythread.run()
main is running
----------------
main call mythread.start()
mythread is running
Java多线程系列–“基础篇”03之 Thread中start()和run()的区别
setPriority(): 更改线程的优先级。
优先级:只能反映 线程 的 中或者是 紧急程度,不能决定是否一定先执行.
//// 1~10 1最低 10最高 5是默认值
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
用法:
Thread4 t1 = new Thread4("t1");
Thread4 t2 = new Thread4("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
package t1;
public class StatusThread3 implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("自定义的线程执行了....");
try {
//wait方法之后一直处于等待状态,直到调用了notify或notifyall
//如果没有synchronized则不能wait或notify
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
StatusThread3 n = new StatusThread3();
// 初始化状态
Thread thread = new Thread(n); // 创建线程,并指定线程任务
thread.start(); // 启动线程
while (true) {
System.out.println("主线程执行了...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
n.notifyAll();
}
}
}
执行报错信息如下:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at course.threadstu.t1.StatusThread3.run(StatusThread3.java:11)
at java.lang.Thread.run(Thread.java:745)
主线程执行了...
自定义的线程执行了....
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at course.threadstu.t1.StatusThread3.main(StatusThread3.java:33)
Process finished with exit code 1
package t1;
public class StatusThread4 implements Runnable {
@Override
public synchronized void run() {
while(true) {
System.out.println("自定义的线程执行了....");
try {
//wait方法之后一直处于等待状态,直到调用了notify或notifyall
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
StatusThread4 n = new StatusThread4();
// 初始化状态
Thread thread = new Thread(n); // 创建线程,并指定线程任务
thread.start(); // 启动线程
while(true) {
synchronized (n) {
System.out.println("主线程执行了...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
n.notifyAll();
}
}
}
}
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
public final void wait() throws InterruptedException {
wait(0);
}
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final native void notify();
public final native void notifyAll();
}
join():指等待t线程终止
使用方式。
join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
Thread t = new AThread();
t.start();
t.join();
为什么要用join()方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,
但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,
这个时候就要用到join()方法了。
示例
class Thread1 extends Thread {
private String name;
public Thread1(String name) {
super(name);
this.name = name;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程" + name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
}
public class Thread1Test1 {
/**
* main主线程运行开始!
* main主线程运行结束!
* B 线程运行开始!
* 子线程B运行 : 0
* A 线程运行开始!
* 子线程A运行 : 0
* 子线程B运行 : 1
* 子线程A运行 : 1
* 子线程B运行 : 2
* 子线程A运行 : 2
* 子线程B运行 : 3
* 子线程A运行 : 3
* 子线程B运行 : 4
* 子线程A运行 : 4
* B 线程运行结束!
* A 线程运行结束!
*
* 主线程结束的比子线程早
*/
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
mTh1.start();
mTh2.start();
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
}
}
public class Thread1Test2 {
/**
main主线程运行开始!
A 线程运行开始!
B 线程运行开始!
子线程A运行 : 0
子线程B运行 : 0
子线程A运行 : 1
子线程B运行 : 1
子线程A运行 : 2
子线程B运行 : 2
子线程A运行 : 3
子线程B运行 : 3
子线程B运行 : 4
子线程A运行 : 4
B 线程运行结束!
A 线程运行结束!
main主线程运行结束!
* 主线程结束的比子线程早
*/
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
mTh1.start();
mTh2.start();
//wait();
try {
System.out.println("mTh1=->"+mTh1.isAlive());
mTh1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
}
}
Thread1Test1中的主线程,两个子线程是各自运行的,
Thread1Test2中的两个子线程join到主线程后,是两个子线程完成之后主线程才完成的.
只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束是,守护线程随着JVM一同结束工作,Daemon作用是为其他线程提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),他就是一个很称职的守护者。
优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务
生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。
线程类型:用户线程和守护线程。Thread.setDaemon(false)用户线程[默认];setDaemon(true)设置为守护线程。
用户线程和守护线程的关系
1.主线程结束,如果还有用户线程运行,JVM存活;
2.主线程结束,如果没有用户线程运行,则守护线程结束,JVM也结束
example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
示例
public class NewThreadExtendAndDaemon extends Thread {
public NewThreadExtendAndDaemon(String name) {
super(name);
}
@Override
public void run() {
while(true) {
System.out.println(getName() + "线程执行了 .. ");
}
}
public static void main(String[] args) {
NewThreadExtendAndDaemon d1 = new NewThreadExtendAndDaemon("first-thread");
NewThreadExtendAndDaemon d2 = new NewThreadExtendAndDaemon("second-thread");
d1.setDaemon(true);//守护线程
d2.setDaemon(true);//守护线程
d1.start();
d2.start();
try{
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
}
}
//以上线程d1和d2会随着主线程的离开(执行完毕)而退出. 随后JVM也会退出.
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。可看上面的图。
返回线程的上次的中断状态,并清除中断状态,不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!
public class NewThreadExtendAndInterrupt extends Thread {
public NewThreadExtendAndInterrupt(String name) {
super(name);
}
@Override
public void run() {
while (!interrupted()) {//和interrupt();搭配使用
System.out.println(getName() + "线程执行了 .. ");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NewThreadExtendAndInterrupt d1 = new NewThreadExtendAndInterrupt("first-thread");
NewThreadExtendAndInterrupt d2 = new NewThreadExtendAndInterrupt("second-thread");
d1.start();
d2.start();
d1.interrupt();//JDK6才出现
}
}
执行结果
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
java线程中的interrupt,isInterrupt,interrupted方法
stop方法天生就不安全,因为它在终止一个线程时会强制中断线程的执行,不管run方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象
public class NewThreadExtendAndStop extends Thread {
public NewThreadExtendAndStop(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "线程执行了 .. ");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NewThreadExtendAndStop d1 = new NewThreadExtendAndStop("first-thread");
NewThreadExtendAndStop d2 = new NewThreadExtendAndStop("second-thread");
d1.start();
d2.start();
d1.stop(); //已过期( @Deprecated).
}
}
运行结果如下:
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
second-thread线程执行了 ..
......
suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。
java多线程(八)为什么弃用stop和suspend
package java.lang;
public
class Thread implements Runnable {
////源码Thread类中优先级:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
////不同操作系统对应的优先级不同. window1-10.linux?
private volatile char name[];
private int priority;
private Thread threadQ;
/* What will be run. */
private Runnable target;
* The group of this thread */
private ThreadGroup group;
//daemon /'dimən/ 守护进程;后台程序
private boolean daemon = false;
/*
* The requested stack size for this thread, or 0 if the creator did
* not specify a stack size. It is up to the VM to do whatever it
* likes with this number; some VMs will ignore it.
*/
////栈的大小,如果没指定默认为(一些VM会用这个值,一些VM忽略这个)
private long stackSize;
////构造方法1
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
////构造方法2 根据name的构造方法,init(ThreadGroup g,Runable target,String name,long stackSize).
public Thread(String name) {
init(null, null, name, 0);
}
////构造方法3--传入Runnable
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null);
}
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
@Override
public void run() {
if (target != null) {
target.run();
}
}
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);//如果子线程存活则让当前线程wait
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
/* Some private helper methods */
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);
}
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
package java.lang;
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {}
//如果ThreadGroup分组为空,则用parent.getThreadGroup();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
//// activeGroupCount(活跃的线程数目).
public int activeGroupCount() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
int n = ngroupsSnapshot;
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
n += groupsSnapshot[i].activeGroupCount();
}
return n;
}
////interrupt(中断线程组的所有线程)
public final void interrupt() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
for (int i = 0 ; i < nthreads ; i++) {
threads[i].interrupt();
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
groupsSnapshot[i].interrupt();
}
}
}
主线程:JVM调用程序main()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
线程类的一些常用方法:
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
synchronized(A.class){}
对类的class即:class literal(类名称字面常量)加锁,即对所有此所属的类加锁
对类加锁,即对所有此类的对象加锁,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
synchronized的方法可以称为同步方法,对类对象(object reference)加锁,即对所有此类的对象加锁,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。
这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
synchronized的代码可以称为同步代码块,这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;
或
public void method3(SomeObject so)
{
synchronized(so){//...}
}
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA(){
synchronized(lock) { //… }
}
}
注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
public class SyncObject {
private byte[] lock = new byte[0];
public synchronized void methodA() {//对当前对象加锁
}
public void methodB() {
synchronized (this) {
}//对当前对象加锁,与methodA用法相同
}
public void methodB2(Object o) {
synchronized (o) {
}//对o加锁
}
/**
对lock加锁,零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:
生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
*/
public void methodB3() {
synchronized (lock) {
}
}
public static synchronized void methodC() {
}//对类加锁,即对所有此类的对象加锁
public void methodD() {
synchronized (SyncObject.class) {
}//对类加锁,即对所有此类的对象加锁
}
}
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。
但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。
由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
在创建线程时,必须要建立一个Thread类的或其子类的实例。
因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,
以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
public class MyThread1 extends Thread {
private String name;
public MyThread1(String name) {
this.name = name;
}
public void run() {
System.out.println("hello " + name);
}
public static void main(String[] args) {
Thread thread = new MyThread1("world");
thread.start();
}
}
由于这种方法是在创建线程对象的同时传递数据的,这样就不会造成数据在线程运行后才传入的现象。
如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。
由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。
因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,
另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。
下面的代码是对MyThread1类的改版,使用了一个setName方法来设置 name变量:
public class MyThread2 implements Runnable {
private String name;
public void setName(String name) {
this.name = name;
}
public void run() {
System.out.println("hello " + name);
}
public static void main(String[] args) {
MyThread2 myThread = new MyThread2();
myThread.setName("world");
Thread thread = new Thread(myThread);
thread.start();
}
}
上面两种方法都是main方法中主动将数据传入线程类的。
这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,
如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。
从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个 value是无法事先就传入线程类的。
class Data {
public int value = 0;
}
class Work {
public void process(Data data, Integer... numbers) {
for (int n : numbers) {
data.value += n;
}
}
}
public class MyThread3 extends Thread {
private Work work;
public MyThread3(Work work) {
this.work = work;
}
public void run() {
java.util.Random random = new java.util.Random();
Data data = new Data();
int n1 = random.nextInt(1000);
int n2 = random.nextInt(2000);
int n3 = random.nextInt(3000);
work.process(data, n1, n2, n3); // 使用回调函数
System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+"
+ String.valueOf(n3) + "=" + data.value);
}
public static void main(String[] args) {
Thread thread = new MyThread3(new Work());
thread.start();
}
}
–整理自网络