一、多线程的基本知识
1、创建一个线程的两个方法
- 通过继承Thread类本身。
- 通过实现Runnable接口;
通过继承Thread类的方式有一定的局限性,java中只支持单一一个类,一旦继承了其他类就不能继承Thread类了。
----通过继承thread类创建线程
class MyThread extends Thread {
private int i = 0;
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);//获取线程名称;
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
myThread1.start(); // 调用start()方法使得线程进入就绪状态
myThread2.start(); // 调用start()方法使得线程进入就绪状态
}
}
}
----通过实现Runnable接口创建线程
class MyRunnable implements Runnable {
private int i = 0;
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Thread thread2 = new Thread(myRunnable);
thread1.start(); // 调用start()方法使得线程进入就绪状态
thread2.start();
}
}
}
2、Thread类的一些重要方法
1public void start() |
使该线程开始执行;
Java
虚拟机调用该线程的 run 方法。
|
public void run() |
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
|
public final void setName(String name) |
改变线程名称,使之与参数 name 相同。
|
public final void setDaemon(boolean on) |
将该线程标记为守护线程或用户线程。
|
public final void setPriority(int priority) |
更改线程的优先级。
|
public final void join(long millisec) |
等待该线程终止的时间最长为 millis 毫秒。
|
public void interrupt() |
中断线程。
|
public final boolean isAlive() |
测试线程是否处于活动状态。
|
currentThread() | 获取当前线程 |
getName() | 获取当前线程名字 |
测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法。
public static void yield() |
暂停当前正在执行的线程对象,并执行其他线程。
只有与当前线程优先级相同或者更高的线程才能获得执行机会。
|
public static void sleep(long millisec) |
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
|
public static boolean holdsLock(Object x) |
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
|
public static Thread currentThread() |
返回对当前正在执行的线程对象的引用。
|
public static void dumpStack() |
将当前线程的堆栈跟踪打印至标准错误流。
|
3、Thread类的构造方法
- Thread(Runnable threadOb,String threadName);// threadOb 是一个实现Runnable 接口的类的实例,并且 threadName指定新线程的名字。
4、设置线程的优先级
静态常量 | 说明 |
static int MAX_PRIORITY | 线程可以具有的最高优先级。相当于10 |
static int MIN_PRIORITY | 线程可以具有的最低优先级。相当于1 |
static int NORM_PRIORITY | 分配给线程的默认优先级。相当于5 |
5、线程的生命周期及状态转换
二、多线程的同步
1、线程的安全
多线程的并发执行可以提高程序的效率,但当多个线程去访问同一个资源是,将会引发一些安全问题。
例如:多个窗口的售票系统,可能会在同一个时间卖出了同一张票。
2、同步代码块
Object lock =new Object();
synchronized(lock){
}
lock是一个锁对象,默认情况下标志位为1,当线程执行同步代码块时会将锁对象的标志为置为0,阻止其他线程的调用。
等当前线程执行完同步代码快后,新线程才能进入执行代码块内的内容。
实例
public class ThreadEx3{
public static void main(String[] args) {
Teacher t = new Teacher();
new Thread(t, "窗口1").start();
new Thread(t, "窗口2").start();
new Thread(t, "窗口3").start();
}
}
class Teacher implements Runnable {
private int notes = 80;
Object lock =new Object();
public void run() {
while (true) {
synchronized(lock){
if (notes > 0) {
try {
Thread.sleep(10); // 经过的线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---发出的票数"
+ notes--);
}//if
}//while
}//run()
}//Runnable
3、同步方法
synchronized 返回值类型 方法名(){}
被synchronized修饰的方法在某一时刻只允许一个线程访问,其他的线程都会发生堵塞。
实例
public class ThreadEx3{
public static void main(String[] args) {
Teacher t = new Teacher();
new Thread(t, "窗口1").start();
new Thread(t, "窗口2").start();
new Thread(t, "窗口3").start();
}
}
class Teacher implements Runnable {
private int notes = 80;
public void run() {
while (true) {
dispatchNotes(); // 调用售票方法
if (notes <= 0) {
break;
}
}
}
private synchronized void dispatchNotes() {//同步方法
if (notes > 0) {
try {
Thread.sleep(10); // 经过的线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---发出的票数"
+ notes--);
}
}
}
4、比较和弊端
在同步方法中也有锁的存在,只是在同步方法中的锁不需要手动创建,这就在一点程度上保证了锁的唯一性,同步方法被所以线程共享。
弊端:
线程每次执行同步代码的时候都会判断所得状态,这回消耗大量的资源,效率低下。
三、多线程通信(
等待/通知机制
)
1、解决的问题
轮询的条件的可见性问题。多个线程将会按照一定顺序流执行。
所谓等待/通知机制,就是线程A在执行的时候,需要一个其他线程来提供的结果,但是其他线程还没有告诉他这个结果是什么,于是线程A开始等待,
当其他线程计算出结果之后就将结果通知给线程A,A线程唤醒,继续执行。
2、用到的方法
void wait() | wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。 |
void notify() | notify()方法会唤醒一个等待当前对象的锁的线程。 |
void notifyAll() | 唤醒此同步锁上调用wait()方法的所以线程 |
以上的三个方法的调用者都应是同步锁对象.