一般的な方法をスレッド
方法 | 説明 |
---|---|
静的int型はactiveCount() | スレッドスレッドスレッドグループとそのサブグループの活動の数の現在の推定値を返します。 |
無効開始() | スレッドが実行を開始します。Java仮想マシンは、このスレッドのrunメソッドを呼び出します。 |
静的スレッドcurrentThread() | 実行参照の現在のスレッドを取得します。 |
長いのgetId() | スレッドIDを取得します。 |
文字列のgetName() | スレッド名を取得します。 |
空のsetName(文字列名) | スレッドの名前を変更します。 |
int型getPriority() | スレッドの優先順位の値を取得します。 |
int型setPriorityを(int型優先) | スレッドの優先順位を変更します。 |
Thread.State getStateを() | スレッドの状態を取得します。 |
無効割り込み() | このスレッドを中断 |
ブールisInterruptedを() | スレッドが中断されているかどうかを確認してください。 |
静的ブールは、(中断) | テストは、現在のスレッドが割り込まれているかどうか、このメソッドを呼び出すと、falseに割り込みフラグを復元します。 |
静的ボイド収量() | スケジューラに提示し、現在のスレッドが現在のスレッドのプロセッサを放棄していく所存です。 |
無効実行() | このメソッドは、ランタイム実行スレッドと呼ばれています |
無効スリープ(ロングミリ秒) | 現在実行中のスレッドは、指定されたミリ秒数の(一時的に実行を停止)スリープ状態に。 |
ボイド(参加) | スレッドの死を待っています。 |
空の参加(長いミリ秒) | 死ぬために、スレッドを待つ、指定されたミリ秒数。 |
ブールのisAlive() | このスレッドがアクティブであるかどうか。 |
ボイド(一時停止) | スレッド(非推奨)を一時停止するには。 |
無効履歴書() | 中断されたスレッドを回収する(非推奨)。 |
無効停止() | スレッド(非推奨)を停止します。 |
ブールisDaemon() | このスレッドがデーモンスレッドであるかどうか。 |
空は、setdaemon(ブールB) | スレッドは、startメソッドが呼び出される前に設定され、デーモンスレッドとしてマークされています。 |
4つの方法のスレッドを開始
継承スレッド
class UseThread extends Thread {
@Override
public void run() {
System.out.println("UseThread object run ...");
}
public static void main(String[] args) {
UseThread useThread = new UseThread();
useThread.start();
}
}
Runnableを実装
class UseRunnable implements Runnable {
@Override
public void run() {
System.out.println("UseRunnable object run ...");
}
public static void main(String[] args) {
UseRunnable useRunnable = new UseRunnable();
Thread thread = new Thread(useRunnable);
thread.start();
}
}
コーラブルを実装
class UseCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("UseCallable object call ...");
return "call result";
}
public static void main(String[] args) {
UseCallable callable = new UseCallable();
FutureTask task = new FutureTask(callable);
Thread thread = new Thread(task);
thread.start();
try {
System.out.println("线程运行返回:" + task.get());
} catch (InterruptedException | ExecutionException e) {
task.cancel(true);//取消计算
e.printStackTrace();
}
}
}
エグゼキューターを使用して
class NewThread {
public static void main(String[] args) {
Thread t = Executors.defaultThreadFactory().newThread(() -> System.out.println("使用Executors创建线程..."));
t.start();
}
}
Runnableをを使用するメリットの実現
- 単一継承の制限を回避する、クラスは複数のインターフェイスを実装することができ
- リソースの共有に適して
チケットを販売するためには、例えば、相続スレッドの使用:
class Ticket {
private int ticketNum;
public Ticket(int ticketNum) {
this.ticketNum = ticketNum;
}
public void sell() {
while (true) {
synchronized (this) {
if (ticketNum <= 0) break;
ticketNum--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩[" + ticketNum + "]张票");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TicketThread extends Thread {
private Ticket ticket;
public TicketThread(Ticket ticket, String threadName) {
super(threadName);
this.ticket = ticket;
}
@Override
public void run() {
ticket.sell();
}
public static void main(String[] args) {
//3个线程卖10张票
Ticket ticket = new Ticket(10);
new TicketThread(ticket, "线程1").start();
new TicketThread(ticket, "线程2").start();
new TicketThread(ticket, "线程3").start();
}
}
それはRunnableを実装を実装します。
class TicketRunnable implements Runnable {
private int ticketNum;
public TicketRunnable(int ticketNum) {
this.ticketNum = ticketNum;
}
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticketNum <= 0) break;
ticketNum--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩[" + ticketNum + "]张票");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//3个线程卖10张票
TicketRunnable ticket = new TicketRunnable(10);
new Thread(ticket, "线程1").start();
new Thread(ticket, "线程2").start();
new Thread(ticket, "线程3").start();
}
}
安全停止スレッド方法
public class RunnableImpl implements Runnable {
@Override
public void run() {
//当执行中断方法interrupt()后,线程并不会停止,而是会继续执行完,线程与线程之间是一种协作状态
// while(true){
// System.out.println("UseThread object run ...");
// }
//需要受到控制的话,使用isInterrupted()方法判断一下
// while (!Thread.currentThread().isInterrupted()) {
// System.out.println(Thread.currentThread().getName() + " is run ...");
// }
//使用interrupted()方法判断中断状态会将中断标识恢复成false
// while (!Thread.currentThread().interrupted()) {
// System.out.println(Thread.currentThread().getName() + " is run ...");
// }
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " is run ...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
//抛出InterruptedException异常后中断标识会恢复成false
//如果抛异常后需要中断,需要在调用interrupt()方法进行中断操作
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName() + " interrupt flag is:" + Thread.currentThread().isInterrupted());
}
public static void main(String[] args) throws InterruptedException {
RunnableImpl runnable = new RunnableImpl();
Thread t = new Thread(runnable);
t.start();
Thread.sleep(2000);
t.interrupt();
}
}
スレッドは、スレッド自体によって決定された停止、参照呼び出しが中断()メソッドは、ちょうど挨拶するスレッドするスレッドを使用しています。
スレッドの停止、再開、方法を一時停止、停止方法をお勧めしませんが方法を中断、間違ったスレッドの解放資源につながる可能性があり、スレッドデッドロックが発生するのは簡単です
デーモンスレッド
デーモンスレッドが一緒に死のメインスレッドでスレッドです、試してみるの内容は、finallyブロックは必ずしも実行されません。
デーモンスレッドは:その後、スレッドのボイドは、setdaemon(ブール)TUREに設定する方法、開始を呼び出す()メソッドを呼び出す前に、スレッドの呼び出しを開始()メソッドでは、スレッドは、スレッドの守護者です。
例
/**
* @CalssName DaemonThreadDemo
* @Description 守护进程
* @since JDK 1.8
*/
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("I use Lambda expression...");
}
} finally {
System.out.println("finally code...");
}
});
t.setDaemon(true);
t.start();
Thread.sleep(50);
}
}
スレッドの優先順位
設定したスレッドの優先順位の例
/**
* @CalssName ThreadPriority
* @Description 线程优先级
* @since JDK 1.8
*/
public class ThreadPriority {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
int t1count = 0, t2count = 0;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
synchronized (this) {
String tName = Thread.currentThread().getName();
if ("线程1".equals(tName)) {
t1count++;
System.out.println(tName + " run... " + t1count + "次");
}
if ("线程2".equals(tName)) {
t2count++;
System.out.println(tName + " run... " + t2count + "次");
}
}
}
}
};
Thread t1 = new Thread(runnable, "线程1");
Thread t2 = new Thread(runnable, "线程2");
t1.setPriority(Thread.MAX_PRIORITY);//最大优先级10
t2.setPriority(Thread.MIN_PRIORITY);//最小优先级1
t1.start();
t2.start();
}
}
以下の営業成績
スレッドの優先度が異なるシステムによると、ランタイムの異なる実装が高く設定されていない、CPUは確かに優先されます
内蔵同期ロック
オブジェクトのロック:オブジェクトは、オブジェクトの新しいクラスの外にあるロック
ロック:ロックロックは、各クラスが一つだけClassオブジェクトを持っていることを保証するために、Java仮想マシンClassオブジェクトのクラスであります
ロックとサンプルオブジェクトのロックを実行します
public class SynchronizedDemo {
//对象锁
public synchronized void test1() {
synchronized (this) {
//这也是对象锁
System.out.println("方法中使用对象锁...");
}
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(600);
} catch (InterruptedException ie) {
}
}
}
//类锁
public synchronized static void test2() {
synchronized (SynchronizedDemo.class) {
//这个也是类锁
System.out.println("方法中使用类锁...");
}
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(250);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final SynchronizedDemo myt2 = new SynchronizedDemo();
Thread test1 = new Thread(new Runnable() {
public void run() {
myt2.test1();
}
}, "test1");
Thread test2 = new Thread(new Runnable() {
public void run() {
SynchronizedDemo.test2();
}
}, "test2");
test1.start();
test2.start();
}
}
業績
方法中使用对象锁...
test1 : 4
方法中使用类锁...
test2 : 4
test2 : 3
test2 : 2
test1 : 3
test2 : 1
test2 : 0
test1 : 2
test1 : 1
test1 : 0
揮発性の使用
スレッドセーフではない揮発性、それはデータの視認性を確保することができ、我々はデータの原子性を保証することはできません。
その同時高効率の使用を記述するためのスレッドを読んでのみ、複数のスレッドの場合にも適用下に揮発性。
使用例:
public class VolatileDemo {
private volatile int score;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
//this.score = score + 10;//这种做法错误,因为volatile不能保证score的原子性
}
}
ThreadLocalの使用
この変数のコピーを使用しているThreadLocal変数、各スレッド複数のスレッドがこのThreadLocal変数値、他のスレッドにアクセスすることはできません。
方法 | メソッド説明 |
---|---|
公共Tの取得() | 現在のThreadLocal変数のスレッドの戻り値のコピー |
保護TはinitialValue() | ThreadLocal変数の初期値は、現在のスレッドのコピーを返します。 |
public void remove() | 删除当前线程中ThreadLocal变量副本的值 |
public void set(T value) | 设置当前线程中ThreadLocal变量副本的值 |
下面是ThreadLocal变量和普通变量的区别示例
/**
* @CalssName UseThreadLocal
* @Description ThreadLocalDemo
* @since JDK 1.8
*/
public class UseThreadLocal implements Runnable {
private int count = 0;
private ThreadLocal<Integer> threadLocal = new ThreadLocal() {
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (this) {
for (int i = 1; i <= 3; i++) {
this.count += this.count + i;
threadLocal.set(threadLocal.get() + i);
System.out.println(threadName + " this.count:" + this.count);
System.out.println(threadName + " threadLocal value:" + threadLocal.get());
}
}
}
public static void main(String[] args) {
UseThreadLocal useThreadLocalRunnable = new UseThreadLocal();
new Thread(useThreadLocalRunnable, "线程1").start();
new Thread(useThreadLocalRunnable, "线程2").start();
new Thread(useThreadLocalRunnable, "线程3").start();
}
}
线程的等待与通知
wait():当前线程处于等待状态,并释放锁,既然要释放锁就必须先获得锁,而线程进入synchronized修饰的方法或者代码块中才能获得锁,所以wait()方法只能在synchronized修饰的方法或者代码块中才能使用。
notify():随机通知并唤醒一个使用wait()方法等待的线程。
notifyAll():通知并唤醒所有使用wait()方法等待的线程。
尽量使用notifyAll()方法,因为使用notify()有可能发生信号丢失的情况。
等待与通知的标准范式
- 等待线程获取到对象的锁,调用wait()方法,放弃锁,进入等待队列
- 通知线程获取到对象的锁,调用对象的notify()方法
- 等待线程接受到通知,从等待队列移到同步队列,进入阻塞状态
- 通知线程释放锁后,等待线程获取到锁继续执行
下面是使用wait()、notifyAll和synchronized内置锁做的简易链接池工具
/**
* @CalssName DBPool
* @Description 数据库链接池工具
* @since JDK 1.8
*/
public class DBPool {
private static LinkedList<Connection> pool = new LinkedList<>();
private static String url = "jdbc:mysql://192.168.86.101:3306/mysql";
private static String username = "root";
private static String password = "my-pwd";
/**
* @return java.sql.Connection
* @Title getConnection
* @Description 从链接池中获取链接,如果没有链接了在指定有效时间内获取链接,不然返回null
* @Param [mills] 没有链接时指定获取去时间
* @Throws InterruptedException
*/
public Connection getConnection(long mills) throws InterruptedException {
synchronized (pool) {
if (mills <= 0) {
while (pool.isEmpty()) {
//链接被用光了,等待回收链接
pool.wait();
}
//有链接了
return pool.removeFirst();
} else {
long overTime = System.currentTimeMillis() + mills;
long remain = mills;
while (pool.isEmpty() && remain > 0) {
//没链接了,等待指定时间
pool.wait(remain);
remain = overTime - System.currentTimeMillis();
}
// 指定时间后还没有链接就返回null
if (pool.isEmpty()) return null;
return pool.removeFirst();
}
}
}
/**
* @CalssName DBPool
* @Description 释放链接
* @since JDK 1.8
*/
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool) {
pool.addLast(connection);
//链接回收了,通知其他等待的线程使用
pool.notifyAll();
}
}
}
/**
* @CalssName DBPool
* @Description 初始化链接池
* @since JDK 1.8
*/
public DBPool(int initSize) {
if (initSize > 0) {
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < initSize; i++) {
Connection connection = DriverManager.getConnection(url, username, password);
pool.add(connection);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new DBPool(10);
System.out.println(pool.size());
}
}
Join方法
如果线程A调用线程B的join()方法之后就必须等待线程B执行完,线程A才会执行
示例
/**
* @CalssName ThreadUseJoin
* @Description 使用join
* @since JDK 1.8
*/
public class ThreadUseJoin {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ",i:" + i);
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
t1.join();//等t1线程执行完销毁才开始执行
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ",i:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
t2.start();
}
}
Yield方法
yield方法执行后并不会释放锁,而是放弃当前在CPU的运行权力,等待下一次CPU调度执行。
执行sleep和notify、notifyAll方法也不会释放锁,notify、notifyAll执行之后唤醒其他线性,但唤醒的以及其他线程仍然处于阻塞状态,直到当前synchronized修饰的代码执行完其他线程才会获取锁